/*
 * This file is part of aion-unique <aion-unique.com>.
 *
 *  aion-unique is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  aion-unique is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with aion-unique.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionemu.gameserver.model.gameobjects.stats.listeners;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.stats.CreatureGameStats;
import com.aionemu.gameserver.model.gameobjects.stats.StatEnum;
import com.aionemu.gameserver.model.gameobjects.stats.modifiers.AddModifier;
import com.aionemu.gameserver.model.gameobjects.stats.modifiers.RateModifier;
import com.aionemu.gameserver.model.gameobjects.stats.modifiers.SetModifier;
import com.aionemu.gameserver.model.gameobjects.stats.modifiers.StatModifier;
import com.aionemu.gameserver.model.items.ItemSlot;
import com.aionemu.gameserver.model.items.ManaStone;
import com.aionemu.gameserver.model.templates.item.ItemTemplate;
import com.aionemu.gameserver.model.templates.itemset.ItemSetTemplate;
import com.aionemu.gameserver.model.templates.itemset.PartBonus;
import com.aionemu.gameserver.services.EnchantService;

/**
 * @author xavier
 */
public class ItemEquipmentListener
{
	//private static final Logger	log	= Logger.getLogger(ItemEquipmentListener.class);

	/**
	 * 
	 * @param itemTemplate
	 * @param slot
	 * @param cgs
	 */
	private static void onItemEquipment(Item item, int slot, CreatureGameStats<?> cgs)
	{
		List<StatModifier> allModifiers = new ArrayList<StatModifier>();
		List<StatModifier> modifiers = item.getItemTemplate().getModifiers();
		if (modifiers != null)
		{
			if (item.getEquipmentSlot() == ItemSlot.MAIN_HAND.getSlotIdMask() || item.getEquipmentSlot() == ItemSlot.OFF_HAND.getSlotIdMask())
			{
				for (StatModifier modifier : modifiers)
				{
					switch (modifier.getStat())
					{
						case ATTACK_SPEED:
						case PVP_ATTACK_RATIO:
						case BOOST_CASTING_TIME:
							continue;
						case PHYSICAL_ATTACK:
							if (item.getEquipmentSlot() == ItemSlot.MAIN_HAND.getSlotIdMask())
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_ATTACK_MAIN_HAND));
							else
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_ATTACK_OFF_HAND));
							break;
						case PHYSICAL_CRITICAL:
							if (item.getEquipmentSlot() == ItemSlot.MAIN_HAND.getSlotIdMask())
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_CRITICAL_MAIN_HAND));
							else
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_CRITICAL_OFF_HAND));
							break;
						case PHYSICAL_ACCURACY:
							if (item.getEquipmentSlot() == ItemSlot.MAIN_HAND.getSlotIdMask())
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_ACCURACY_MAIN_HAND));
							else
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_ACCURACY_OFF_HAND));
							break;
						default:
							allModifiers.add(modifier);
					}
				}
			}
			else
				allModifiers.addAll(modifiers);
		}
		cgs.addModifiers(item, allModifiers);
	}

	/**
	 * @param item
	 * @param cgs
	 */
	public static void onItemEquipment(Item item, Player owner)
	{
		synchronized (item)
		{
			ItemTemplate itemTemplate = item.getItemTemplate();
			onItemEquipment(item, item.getEquipmentSlot(), owner.getGameStats());

			// Check if belongs to ItemSet
			if (itemTemplate.isItemSet())
				recalculateItemSet(itemTemplate.getItemSet(), owner);

			if (item.hasManaStones())
				addStonesStats(item.getItemStones(), owner.getGameStats(), item);
			if (item.hasFusionedItem())
				addStonesStats(item.getFusionStones(), owner.getGameStats(), item);

			addGodstoneEffect(owner, item);

			EnchantService.onItemEquip(owner, item);
		}
	}

	public static void addItemStat(Player player, Item item)
	{

	}

	/**
	 * 
	 * @param itemSetTemplate
	 * @param player
	 */
	private static void recalculateItemSet(ItemSetTemplate itemSetTemplate, Player player)
	{
		if (itemSetTemplate == null)
			return;

		player.getGameStats().endEffect(itemSetTemplate);
		// 1.- Check equipment for items already equip with this itemSetTemplate id
		int itemSetPartsEquipped = player.getEquipment().itemSetPartsEquipped(itemSetTemplate.getId());

		// 2.- Check Item Set Parts and add effects one by one if not done already
		for (PartBonus itempartbonus : itemSetTemplate.getPartbonus())
		{
			// If the partbonus was not applied before, do it now
			if (itempartbonus.getCount() <= itemSetPartsEquipped)
			{
				player.getGameStats().addModifiers(itemSetTemplate, itempartbonus.getModifiers());
			}
		}

		// 3.- Finally check if all items are applied and set the full bonus if not already applied
		if (itemSetTemplate.getFullbonus() != null && itemSetPartsEquipped == itemSetTemplate.getFullbonus().getCount())
		{
				// Add the full bonus with index = total parts + 1 to avoid confusion with part bonus equal to number of
				// objects
				player.getGameStats().addModifiers(itemSetTemplate, itemSetTemplate.getFullbonus().getModifiers());
		}
	}

	/**
	 * All modifiers of stones will be applied to character
	 * 
	 * @param itemStones
	 * @param cgs
	 */
	private static void addStonesStats(Set<ManaStone> itemStones, CreatureGameStats<?> cgs, Item item)
	{
		if (itemStones == null || itemStones.size() == 0)
			return;

		for (ManaStone stone : itemStones)
		{
			addStoneStats(stone, cgs, item);
		}
	}

	/**
	 * All modifiers of stones will be removed
	 * 
	 * @param itemStones
	 * @param cgs
	 */
	public static void removeStoneStats(Set<ManaStone> itemStones, CreatureGameStats<?> cgs)
	{
		if (itemStones == null || itemStones.size() == 0)
			return;

		for (ManaStone stone : itemStones)
		{
			List<StatModifier> modifiers = stone.getModifiers();
			if (modifiers != null)
			{
				cgs.endEffect(stone);
			}
		}
	}

	/**
	 *  Used when socketing of equipped item
	 *  
	 * @param stone
	 * @param cgs
	 */
	public static void addStoneStats(ManaStone stone, CreatureGameStats<?> cgs, Item item)
	{
		List<StatModifier> modifiers = stone.getModifiers();
		List<StatModifier> allModifiers = new ArrayList<StatModifier>();
		if (modifiers != null)
		{
			if (item.getEquipmentSlot() == ItemSlot.MAIN_HAND.getSlotIdMask() || item.getEquipmentSlot() == ItemSlot.OFF_HAND.getSlotIdMask())
			{
				for (StatModifier modifier : modifiers)
				{
					switch (modifier.getStat())
					{
						case PHYSICAL_ATTACK:
							if (item.getEquipmentSlot() == ItemSlot.MAIN_HAND.getSlotIdMask())
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_ATTACK_MAIN_HAND));
							else
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_ATTACK_OFF_HAND));
							break;
						case PHYSICAL_CRITICAL:
							if (item.getEquipmentSlot() == ItemSlot.MAIN_HAND.getSlotIdMask())
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_CRITICAL_MAIN_HAND));
							else
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_CRITICAL_OFF_HAND));
							break;
						case PHYSICAL_ACCURACY:
							if (item.getEquipmentSlot() == ItemSlot.MAIN_HAND.getSlotIdMask())
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_ACCURACY_MAIN_HAND));
							else
								allModifiers.add(cloneModifier(modifier, StatEnum.PHYSICAL_ACCURACY_OFF_HAND));
							break;
						default:
							allModifiers.add(modifier);
					}
				}
			}
			else
			{
				allModifiers.addAll(modifiers);
			}
			cgs.addModifiers(stone, allModifiers);
		}
	}

	/**
	 * 
	 * @param item
	 * @param owner
	 */
	public static void onItemUnequipment(Item item, Player owner)
	{
		synchronized (item)
		{
			// Check if belongs to an ItemSet
			if (item.getItemTemplate().isItemSet())
				recalculateItemSet(item.getItemTemplate().getItemSet(), owner);

			owner.getGameStats().endEffect(item);

			if (item.hasManaStones())
				removeStoneStats(item.getItemStones(), owner.getGameStats());
			if (item.hasFusionStones())
				removeStoneStats(item.getFusionStones(), owner.getGameStats());

			removeGodstoneEffect(owner, item);
		}
	}

	/**
	 * @param item
	 */
	private static void addGodstoneEffect(Player player, Item item)
	{
		if (item.getGodStone() != null)
		{
			item.getGodStone().onEquip(player);
		}
	}

	/**
	 * @param item
	 */
	private static void removeGodstoneEffect(Player player, Item item)
	{
		if (item.getGodStone() != null)
		{
			item.getGodStone().onUnEquip(player);
		}
	}

	private static StatModifier cloneModifier(StatModifier modifier, StatEnum newStat)
	{
		if (modifier instanceof AddModifier)
			return AddModifier.newInstance(newStat, ((AddModifier) modifier).getValue(), ((AddModifier) modifier).getPriority(),
					((AddModifier) modifier).isBonus(), modifier.getConditions());
		else if (modifier instanceof RateModifier)
			return RateModifier.newInstance(newStat, ((RateModifier) modifier).getValue(), ((RateModifier) modifier).getPriority(),
					((RateModifier) modifier).isBonus(), modifier.getConditions());
		else if (modifier instanceof SetModifier)
			return SetModifier.newInstance(newStat, ((SetModifier) modifier).getValue(), ((SetModifier) modifier).getPriority(),
					((SetModifier) modifier).isBonus(), modifier.getConditions());
		return null;
	}
}
